package com.winit.page.manager;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.winit.common.base.BaseController;
import com.winit.common.base.ExecuteResult;
import com.winit.common.util.ListUtil;
import com.winit.commons.util.HttpClientUtil;
import com.winit.commons.util.NumberUtil;
import com.winit.commons.util.SeqGenerate;
import com.winit.config.model.Config;
import com.winit.config.service.IConfigService;
import com.winit.feature.manager.PathContainer;
import com.winit.feature.service.IFeatureService;
import com.winit.indexing.model.Indexing;
import com.winit.indexing.model.IndexingId;
import com.winit.indexing.service.IIdexingService;
import com.winit.menu.model.MatrixMenu;
import com.winit.menu.service.IMenuService;
import com.winit.page.model.Page;
import com.winit.page.model.PageElement;
import com.winit.page.repository.IPageElementRepositoryExt;
import com.winit.page.repository.PageElementRepository;
import com.winit.page.repository.PageOperations;
import com.winit.page.repository.PageRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

@Component
public class PageManager {

    private static Logger logger = LoggerFactory.getLogger(PageManager.class);

    @Value("${url.wsAgent.page.recreate}")
    private String wsAgentPageRecreateUrl;

    @Autowired
    private PageRepository pageRepository;
    @Autowired
    private PageElementRepository pageElementRepository;
    @Autowired
    private IConfigService configService;
    @Autowired
    private IPageElementRepositoryExt pageElementRepositoryExt;
    @Autowired
    private IFeatureService featureService;
    @Autowired
    private IIdexingService indexingService;
    @Autowired
    private PageOperations pageOperations;
    @Autowired
    private IMenuService menuService;
    @Autowired
    private PathContainer pathContainer;

    public ExecuteResult getMenuPages(String menuId, boolean setPath) {
        return getMenuPages(menuId, null, setPath);
    }

    public ExecuteResult getMenuPages(String menuId, String pageId, boolean setPath) {
        ExecuteResult executeResult = new ExecuteResult();
        List<Page> menuPages = new ArrayList<>();
        if (!StringUtils.isEmpty(pageId)) {
            menuPages.add(this.pageRepository.findById(pageId));
        } else {
            menuPages = this.pageRepository.findByRefMenuId(menuId);
        }
        if (CollectionUtils.isEmpty(menuPages)) {
            return executeResult;
        }
        Iterator<Page> iterator = menuPages.iterator();
        Page index = null;
        while (iterator.hasNext()) {
            Page temp = iterator.next();
            if (temp.getFileName().toLowerCase().startsWith("index")) {
                index = temp;
                iterator.remove();
                break;
            }
        }
        ArrayList<Page> pages = new ArrayList<>();
        if (null != index) {
            pages.add(index);
        }
        if (!CollectionUtils.isEmpty(menuPages)) {
            pages.addAll(menuPages);
        }
        if ((!CollectionUtils.isEmpty(pages)) && setPath) {
            for (Page page : pages) {
                page.setMenuPath(this.pathContainer.getPathById(page.getId(), false));
            }
        }
        executeResult.setData(pages);
        return executeResult;
    }

    public ExecuteResult getAllPages() {
        ExecuteResult executeResult = new ExecuteResult();
        List<Page> pages = this.pageRepository.findAllPage();
        if ((!CollectionUtils.isEmpty(pages))) {
            for (Page page : pages) {
                page.setMenuPath(this.pathContainer.getPathById(page.getId(), true));
            }
        }
        executeResult.setData(pages);
        return executeResult;
    }

    public ExecuteResult savePage(Page page) {
        ExecuteResult executeResult = new ExecuteResult();
        if (!StringUtils.isEmpty(page.getId())) {
            logger.info("page updation executed.");
            this.pageOperations.updatePropertiesNotNullById(page);
            if ((!StringUtils.isEmpty(page.getMenuId())) || (!StringUtils.isEmpty(page.getTitle()))) {
                Page dbOne = this.pageRepository.findOne(page.getId());
                this.pathContainer.updatePath(dbOne.getId(), dbOne.getMenuId(), dbOne.getTitle(), dbOne.getChildren());
            }
            return executeResult;
        }


        logger.info("page creation will be executed..");
        String menuId = page.getMenuId();
        if (StringUtils.isEmpty(menuId)) {
            executeResult.setCode(BaseController.REQUEST_ERROR);
            executeResult.setMsg("menu_id不能为空");
            return executeResult;
        }
        page.setId(SeqGenerate.createId("MP"));

        MatrixMenu refMenu = this.menuService.getMenu(menuId, null);
        if (null == refMenu) {
            executeResult.setCode(BaseController.REQUEST_ERROR);
            executeResult.setMsg("指定的menu不存在，menuId:" + menuId);
            return executeResult;
        }
        NumberUtil.SysEnum sysEnum = null;
        String sys = refMenu.getSys();
        if ("tom".equalsIgnoreCase(sys)) {
            sysEnum = NumberUtil.SysEnum.TOM;
        } else if ("sellerportal".equalsIgnoreCase(sys)) {
            sysEnum = NumberUtil.SysEnum.OPC;
        } else if ("matrix".equalsIgnoreCase(sys)) {
            sysEnum = NumberUtil.SysEnum.MATRIX;
        } else {
            logger.error("unsupported sys for:" + sys);
            executeResult.setCode(BaseController.REQUEST_ERROR);
            executeResult.setMsg("系统" + menuId);
            return executeResult;
        }
        List<Page> menuPages = this.pageRepository.findByRefMenuId(menuId);
        int index = null == menuPages ? 1 : menuPages.size() + 1;
        try {
            String code = NumberUtil.createNumber(sysEnum, refMenu.getMenuNum(), NumberUtil.TypeEnum.PAGE, index);
            page.setFileName(code);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            executeResult.setCode(BaseController.REQUEST_ERROR);
            executeResult.setMsg("create number code(file name) error");
            return executeResult;
        }
        page.setChildren(ListUtil.addChild(null, "PROTOTYPE" + page.getId()));
        this.pageRepository.save(page);
        this.pathContainer.add(page.getId(), page.getMenuId(), page.getTitle(), page.getChildren());
        menuService.updateMenuChild(page.getMenuId(), page.getId(), "add");
        this.featureService.createPrototypeForPage(page);
        executeResult.setData(page);
        return executeResult;
    }

    public ExecuteResult getPageElements(String pageId) {
        ExecuteResult executeResult = new ExecuteResult();
        List<PageElement> rootElements = this.pageElementRepository.findByRefPAndPageId(pageId);
        if (CollectionUtils.isEmpty(rootElements)) {
            return executeResult;
        }
        try {
            for (PageElement pageElement : rootElements) {
                this.getPageElementInfo(pageElement, true);
            }
            executeResult.setData(rootElements);
        } catch (Exception e) {
            executeResult.setCode(BaseController.REQUEST_ERROR);
            executeResult.setMsg(e.getMessage());
        }
        return executeResult;
    }

    public ExecuteResult getPageElementsInfo(String elementId) {
        ExecuteResult executeResult = new ExecuteResult();
        PageElement element = this.pageElementRepository.findOne(elementId);
        if (null == element) {
            return executeResult;
        }
        try {
            this.getPageElementInfo(element, false);
            List<String> children = element.getChildren();
            if (!CollectionUtils.isEmpty(children)) {
                List<PageElement> childs = new ArrayList<>();
                element.setChilds(childs);
                for (String childrenId : children) {
                    PageElement child = this.pageElementRepository.findOne(childrenId);
                    if (null == child) {
                        continue;
                    }
                    childs.add(child);
                    this.getPageElementInfo(child, false);
                }
            }
            executeResult.setData(element);
        } catch (Exception e) {
            executeResult.setCode(BaseController.REQUEST_ERROR);
            executeResult.setMsg(e.getMessage());
        }
        return executeResult;
    }

    public PageElement getPageElementInfo(PageElement pageElement, boolean subRecursion) throws Exception {
        String configId = pageElement.getMasterConfigId();
        if (!StringUtils.isEmpty(configId)) {
            Config config = this.configService.getConfigMap(configId, false);
            Map<String, String> configMap = config.getKVMap();
            Map<String, Object> frontConfig = new HashMap<>();
            frontConfig.putAll(configMap);
            pageElement.setAttrs(frontConfig);
            String component = configMap.get("component");
            if (!StringUtils.isEmpty(component)) {
                Map<String, Object> componentMap = JSON.parseObject(component, new TypeReference<Map<String, Object>>() {
                });
                frontConfig.put("component", componentMap);
            }
            String attrs = configMap.get("attrs");
            if (!StringUtils.isEmpty(attrs)) {
                List<Map<String, Object>> attrsList = JSON.parseObject(attrs, new TypeReference<List<Map<String, Object>>>() {
                });
                frontConfig.put("attrs", attrsList);
            }
            configMap.remove("component");
            configMap.remove("attrs");
            if (!CollectionUtils.isEmpty(configMap)) {
                for (Map.Entry<String, String> temp : configMap.entrySet()) {
                    if (null != temp.getValue()) {
                        frontConfig.put(temp.getKey(), JSON.parse(temp.getValue()));
                    } else {
                        frontConfig.put(temp.getKey(), null);
                    }
                }
            }
        }
        if (subRecursion) {
            List<String> children = pageElement.getChildren();
            if (!CollectionUtils.isEmpty(children)) {
                List<PageElement> childs = new ArrayList<>();
                pageElement.setChilds(childs);
                for (String childrenId : children) {
                    PageElement child = this.pageElementRepository.findOne(childrenId);
                    if (null == child) {
                        continue;
                    }
                    childs.add(child);
                    this.getPageElementInfo(child, subRecursion);
                }
            }
        }
        return pageElement;
    }

    public ExecuteResult createPageElement(PageElement pageElement) {
        ExecuteResult executeResult = new ExecuteResult();
        Map<String, String> configMap = new HashMap<>();
        Map<String, Object> newConfig = pageElement.getAttrs();
        for (Map.Entry<String, Object> entry : newConfig.entrySet()) {
            Object value = entry.getValue();
            if (null != value) {
                configMap.put(entry.getKey(), JSONObject.toJSONString(value));
            } else {
                configMap.put(entry.getKey(), null);
            }
        }
        Config config = new Config();
        config.setKVMap(configMap);
        config = this.configService.saveConfig(config);
        pageElement.setMasterConfigId(config.getId());
        if (StringUtils.isEmpty(pageElement.getId())) {
            pageElement.setId(SeqGenerate.createId("PEL"));
        }

        String parentId = pageElement.getParentId();
        if ((!StringUtils.isEmpty(parentId)) && (!"0".equalsIgnoreCase(parentId))) {
            PageElement parent = this.pageElementRepository.findOne(parentId);
            if (null == parent) {
                executeResult.setCode(BaseController.REQUEST_ERROR);
                executeResult.setMsg("defined parent not found for id:" + parentId);
                return executeResult;
            }
            List<String> parentChildren = parent.getChildren();
            PageElement parentUpdate = new PageElement();
            parentUpdate.setId(parentId);
            if (null == parentChildren) {
                parentChildren = new ArrayList<>();
            }
            parentUpdate.setChildren(parentChildren);
            Integer index = pageElement.getIndex();
            index = null == index ? 1 : index;
            if (index > parentChildren.size()) {
                parentChildren.add(pageElement.getId());
            } else {
                parentChildren.add(index - 1, pageElement.getId());
            }
            this.pageElementRepositoryExt.updatePropertiesNotNullById(parentUpdate);
        }
        if (StringUtils.isEmpty(parentId)) {
            pageElement.setParentId("0");
        }
        this.pageElementRepository.save(pageElement);
        executeResult.setData(pageElement.getId());
        return executeResult;
    }

    public ExecuteResult updatePageElementInfo(String elementId, String elementAttrs) {
        ExecuteResult executeResult = new ExecuteResult();
        if (StringUtils.isEmpty(elementAttrs)) {
            return executeResult;
        }
        PageElement pageElement = this.pageElementRepository.findOne(elementId);
        String configId = pageElement.getMasterConfigId();
        Config config = new Config();
        config.setId(configId);
        Map<String, String> configMap = new HashMap<>();
        configMap.put("attrs", elementAttrs);
        config.setKVMap(configMap);
        config = this.configService.saveConfig(config);
        if (StringUtils.isEmpty(configId)) {
            configId = config.getId();
            PageElement update = new PageElement();
            update.setId(elementId);
            update.setMasterConfigId(configId);
            this.pageElementRepositoryExt.updatePropertiesNotNullById(update);
        }
        return executeResult;
    }

    public ExecuteResult deletePageElement(String elementId) {
        ExecuteResult executeResult = new ExecuteResult();
        PageElement element = this.pageElementRepository.findOne(elementId);
        if (null == element) {
            return executeResult;
        }
        String parentId = element.getParentId();
        if ((!StringUtils.isEmpty(parentId)) && (!"0".equalsIgnoreCase(parentId))) {
            PageElement parent = this.pageElementRepository.findOne(parentId);
            if (null != parent) {
                PageElement parentUpdate = new PageElement();
                parentUpdate.setId(parentId);
                List<String> siblings = parent.getChildren();
                siblings.remove(elementId);
                parentUpdate.setChildren(siblings);
                this.pageElementRepositoryExt.updatePropertiesNotNullById(parentUpdate);
            }
        }
        this.deletePageElement(elementId, true);
        return executeResult;
    }

    private void deletePageElement(String elementId, boolean subRecursion) {
        PageElement element = this.pageElementRepository.findOne(elementId);
        if (null == element) {
            return;
        }
        String configId = element.getMasterConfigId();
        if (!StringUtils.isEmpty(configId)) {
            this.configService.deleteConfigById(configId);
        }
        this.pageElementRepository.delete(elementId);
        if (!subRecursion) {
            return;
        }
        List<String> children = element.getChildren();
        if (!CollectionUtils.isEmpty(children)) {
            for (String childrenId : children) {
                this.deletePageElement(childrenId, subRecursion);
            }
        }
    }

    public ExecuteResult deletePage(String pageId, boolean deleteAllFeature) {
        ExecuteResult executeResult = null;
        executeResult = this.featureService.deleteFeature("PROTOTYPE" + pageId);
        if (!executeResult.isSuccess()) {
            return executeResult;
        }
        if (deleteAllFeature) {
            Indexing pageFeatureIndexing = this.indexingService.getIndexing(new IndexingId("page_feature_indexing", pageId));
            if (null != pageFeatureIndexing && (!CollectionUtils.isEmpty(pageFeatureIndexing.getIndexMap()))) {
                Map<String, String> indexMap = pageFeatureIndexing.getIndexMap();
                for (String featureId : indexMap.keySet()) {
                    this.featureService.deleteFeature(featureId);
                }
            }
        } else {
            executeResult = this.deleteRelationBetweenPageAndFeature(pageId);
            if (!executeResult.isSuccess()) {
                return executeResult;
            }
        }
        Page page = pageRepository.findById(pageId);
        this.pageRepository.delete(pageId);
        this.pathContainer.remove(pageId);


        //找到page的menu 修改child
        menuService.updateMenuChild(page.getMenuId(), pageId, "remove");

        return executeResult;
    }


    private ExecuteResult deleteRelationBetweenPageAndFeature(String pageId) {
        ExecuteResult executeResult = new ExecuteResult();
        IndexingId pageFeatureIndexingId = new IndexingId("page_feature_indexing", pageId);
        Indexing pageFeatureIndexing = this.indexingService.getIndexing(pageFeatureIndexingId);
        if (null == pageFeatureIndexing || CollectionUtils.isEmpty(pageFeatureIndexing.getIndexMap())) {
            return executeResult;
        }
        Map<String, String> pageFeatureIndexMap = pageFeatureIndexing.getIndexMap();
        for (Map.Entry<String, String> entry : pageFeatureIndexMap.entrySet()) {
            try {
                this.indexingService.removeIfExists(new IndexingId("feature_page_indexing", entry.getKey()), new String[]{pageId});
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                executeResult.setCode(BaseController.REQUEST_ERROR);
                executeResult.setMsg(e.getMessage());
                return executeResult;
            }
        }
        this.indexingService.deleteIndex(pageFeatureIndexingId);
        return executeResult;
    }


    /**
     * 将Page的内容推送给ws-agent, 实现代码的变更.
     *
     * @param pageId
     * @return
     */
    public ExecuteResult applyPageConfig(String pageId) {
        ExecuteResult executeResult = this.getPageElements(pageId);
        if (!executeResult.isSuccess()) {
            return executeResult;
        }
        Object obj = executeResult.getData();
        if (null == obj) {
            return executeResult;
        }

        List<PageElement> pageElements = (List<PageElement>) obj;

        List<Map<String, Object>> elements = new ArrayList<>();

        //获取页面配置项(内部实现递归层次)
        for (PageElement pageElement : pageElements) {
            elements.add(this.getElementForWsAgent(pageElement));
        }

        try {
            Page page = pageRepository.findOne(pageId);

            String file_name = page.getFileName();
            String menu_path = page.getMenuPath();
            //
            String workspace = "default";
            Map<String, Object> params = new HashMap<>();
            params.put("file_name", file_name);
            params.put("menu_path", menu_path);
            params.put("workspace", workspace);
            params.put("models", page.getModels());
            params.put("elements", JSONObject.toJSONString(elements));
            String rs = HttpClientUtil.doPost(wsAgentPageRecreateUrl, JSONObject.toJSONString(params));
            Map map = (Map) JSONObject.parse(rs);
            executeResult.setMsg((String) map.get("msg"));
            if (!(Boolean) map.get("isSuccess")) {
                logger.error(map.toString());
                executeResult.setCode(BaseController.REQUEST_ERROR);
            }
        } catch (Exception e) {
            logger.error("执行applyPageConfig出错", e);
            executeResult.setCode(BaseController.REQUEST_ERROR);
            executeResult.setMsg(e.getMessage());

        }
        return executeResult;
    }

    Map<String, Object> getElementForWsAgent(PageElement pageElement) {
        Map<String, Object> element = new HashMap<>();
//        element.put("component", JSONObject.parse((String)(pageElement.getAttrs().get("component"))));
//        element.put("component", pageElement.getAttrs().get("component"));
//        element.put("attrs", pageElement.getAttrs().get("attrs"));
//        element.put("m_text", pageElement.getAttrs().get("m_text"));
        element.putAll(pageElement.getAttrs());
        List<PageElement> children = pageElement.getChilds();
        if (CollectionUtils.isEmpty(children)) {
            return element;
        }
        List<Map<String, Object>> elements = new ArrayList<>();
        element.put("elements", elements);
        for (PageElement temp : children) {
            elements.add(this.getElementForWsAgent(temp));
        }
        return element;
    }

    public void saveChildSort(String id, String sort) {
        Page page = pageRepository.findById(id);
        if (null == page) {
            logger.error("com.winit.page.manager.PageManager.saveChildSort id=" + id + ",sort=" + sort);
            return;
        }
        page.setChildren(ListUtil.saveChild(sort));
        pageOperations.updatePropertiesNotNullById(page);
        pathContainer.updatePath(page.getId(), page.getMenuId(), page.getTitle(), page.getChildren());
    }

}
